use ic_sha3::Keccak256;
use std::vec;

#[test]
fn should_hash_test_vectors() {
    for test_vector in keccak256_test_vectors() {
        let result = Keccak256::hash(&test_vector.msg);
        assert_eq!(
            result, test_vector.digest,
            "Unexpected digest {result:?} for test vector {test_vector:?}"
        );
    }
}

fn keccak256_test_vectors() -> impl Iterator<Item = TestVector> {
    keccak256_kat_test_vectors()
        .into_iter()
        .chain(keccak256_other_test_vectors())
}

/// Test vectors from Known-answer and Monte Carlo test results for Keccak-256
/// See the following files in https://keccak.team/obsolete/KeccakKAT-3.zip:
/// * ShortMsgKAT_256.txt
/// * LongMsgKAT_256.txt
///
/// Note that the padding rules changed between the last version of Keccak-256
/// and the produced test vectors. For this reason only a few samples where the bit size is a multiple of 8 are considered.
///
fn keccak256_kat_test_vectors() -> Vec<TestVector> {
    vec![
        //Len = 8
        TestVector::new(
            "CC",
            "EEAD6DBFC7340A56CAEDC044696A168870549A6A7F6F56961E84A54BD9970B8A",
        ),
        //Len = 16
        TestVector::new(
            "41FB",
            "A8EACEDA4D47B3281A795AD9E1EA2122B407BAF9AABCB9E18B5717B7873537D2",
        ),
        //Len = 24
        TestVector::new(
            "1F877C",
            "627D7BC1491B2AB127282827B8DE2D276B13D7D70FB4C5957FDF20655BC7AC30",
        ),
        //Len = 2048
        TestVector::new(
            "724627916C50338643E6996F07877EAFD96BDF01DA7E991D4155B9BE1295EA7D21C9391F4C4A41C75F77E5D27389253393725F1427F57914B273AB862B9E31DABCE506E558720520D33352D119F699E784F9E548FF91BC35CA147042128709820D69A8287EA3257857615EB0321270E94B84F446942765CE882B191FAEE7E1C87E0F0BD4E0CD8A927703524B559B769CA4ECE1F6DBF313FDCF67C572EC4185C1A88E86EC11B6454B371980020F19633B6B95BD280E4FBCB0161E1A82470320CEC6ECFA25AC73D09F1536F286D3F9DACAFB2CD1D0CE72D64D197F5C7520B3CCB2FD74EB72664BA93853EF41EABF52F015DD591500D018DD162815CC993595B195",
            "EA0E416C0F7B4F11E3F00479FDDF954F2539E5E557753BD546F69EE375A5DE29",
        ),
        //Len = 3056
        TestVector::new(
            "023D91AC532601C7CA3942D62827566D9268BB4276FCAA1AE927693A6961652676DBA09219A01B3D5ADFA12547A946E78F3C5C62DD880B02D2EEEB4B96636529C6B01120B23EFC49CCFB36B8497CD19767B53710A636683BC5E0E5C9534CFC004691E87D1BEE39B86B953572927BD668620EAB87836D9F3F8F28ACE41150776C0BC6657178EBF297FE1F7214EDD9F215FFB491B681B06AC2032D35E6FDF832A8B06056DA70D77F1E9B4D26AE712D8523C86F79250718405F91B0A87C725F2D3F52088965F887D8CF87206DFDE422386E58EDDA34DDE2783B3049B86917B4628027A05D4D1F429D2B49C4B1C898DDDCB82F343E145596DE11A54182F39F4718ECAE8F506BD9739F5CD5D5686D7FEFC834514CD1B2C91C33B381B45E2E5335D7A8720A8F17AFC8C2CB2BD88B14AA2DCA099B00AA575D0A0CCF099CDEC4870FB710D2680E60C48BFC291FF0CEF2EEBF9B36902E9FBA8C889BF6B4B9F5CE53A19B0D9399CD19D61BD08C0C2EC25E099959848E6A550CA7137B63F43138D7B651",
            "6C2A841318066B90A9604D0C8ECCB2986B84A0C8675CD243E96957D26E9C1CFD",
        ),
        //Len = 3560
        TestVector::new(
            "20FF454369A5D05B81A78F3DB05819FEA9B08C2384F75CB0AB6AA115DD690DA3131874A1CA8F708AD1519EA952C1E249CB540D196392C79E87755424FEE7C890808C562722359EEA52E8A12FBBB969DD7961D2BA52037493755A5FA04F0D50A1AA26C9B44148C0D3B94D1C4A59A31ACA15AE8BD44ACB7833D8E91C4B86FA3135A423387B8151B4133ED23F6D7187B50EC2204AD901AD74D396E44274E0ECAFAAE17B3B9085E22260B35CA53B15CC52ABBA758AF6798FBD04ECEECED648F3AF4FDB3DED7557A9A5CFB7382612A8A8F3F45947D1A29CE29072928EC193CA25D51071BD5E1984ECF402F306EA762F0F25282F5296D997658BE3F983696FFA6D095C6369B4DAF79E9A5D3136229128F8EB63C12B9E9FA78AFF7A3E9E19A62022493CD136DEFBB5BB7BA1B938F367FD2F63EB5CA76C0B0FF21B9E36C3F07230CF3C3074E5DA587040A76975D7E39F4494ACE5486FCBF380AB7558C4FE89656335B82E4DB8659509EAB46A19613126E594042732DD4C411F41AA8CDEAC71C0FB40A94E6DA558C05E77B6182806F26D9AFDF3DA00C69419222C8186A6EFAD600B410E6CE2F2A797E49DC1F135319801FA6F396B06F975E2A190A023E474B618E7",
            "0EC8D9D20DDF0A7B0251E941A7261B557507FF6287B504362A8F1734C5A91012",
        ),
    ]
}

/// Taken a few samples from https://emn178.github.io/online-tools/keccak_256.html
fn keccak256_other_test_vectors() -> Vec<TestVector> {
    vec![
        TestVector::new(
            "",
            "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
        ),
        TestVector::new(
            "48656C6C6F",
            "06b3dfaec148fb1bb2b066f10ec285e7c9bf402ab32aa78a5d38e34566810cd2",
        ),
        TestVector::new(
            "48656C6C6F20776F726C6421",
            "ecd0e108a98e192af1d2c25055f4e3bed784b5c877204e73219a5203251feaab",
        ),
    ]
}

#[derive(Debug)]
struct TestVector {
    /// Message in hexadecimal format.
    msg: Vec<u8>,
    /// Digest in hexadecimal format.
    digest: [u8; 32],
}

impl TestVector {
    fn new<M: AsRef<[u8]>, D: AsRef<[u8]>>(msg: M, digest: D) -> Self {
        Self {
            msg: hex::decode(msg).expect("valid hex string"),
            digest: hex::decode(digest)
                .expect("valid hex string")
                .try_into()
                .expect("digest must have length 32"),
        }
    }
}
